/**
 * Copyright (c) 2017-2018, zengjintao (1913188966@qq.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.fast.framework.web.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fast.framework.aop.GuiceManager;
import com.fast.framework.aop.InteceptorManager;
import com.fast.framework.aop.Interceptor;
import com.fast.framework.aop.annotation.InteceptorConfig;
import com.fast.framework.constant.Constant;
import com.fast.framework.kit.PropKit;
import com.fast.framework.kit.StrKit;
import com.fast.framework.log.Logger;
import com.fast.framework.util.PackageUtil;
import com.fast.framework.web.annotation.ConfigurationProperties;
import com.fast.framework.web.annotation.Controller;
import com.fast.framework.web.annotation.RequestMapping;
import com.fast.framework.web.config.JFastConfig;
import com.fast.framework.web.config.JfastConfigManeger;
import com.fast.framework.web.orm.DataType;
import com.fast.framework.web.orm.DefaultDataSource;
import com.fast.framework.web.orm.config.DataSourceConfig;
import com.fast.framework.web.orm.config.DataSourceConfigManager;
import com.fast.framework.web.orm.config.DataSourceManager;
import com.fast.framework.web.orm.config.DbConfig;
import com.fast.framework.web.plugin.IPlugin;
import com.fast.framework.web.plugin.PluginManager;


public class Config {
	
    private static final PluginManager pluginManger = PluginManager.getPluginManger();
	private static final InteceptorManager inteceptorManager = InteceptorManager.getInteceptorManager();
	private List<Class<?>> controllerClass = new ArrayList<>();
	private static final Logger log = Logger.getLogger(JFastWebListener.class);
	private static final Config config = new Config();
	private static final JFastConfig jfastConfig = JFastConfig.getJfastConfig();
	private final Map<String, Action> mapping = new HashMap<String, Action>();
	private PropKit propKit = null;
	
	public static Config getConfig(){
		return config;
	}
	
	public static JFastConfig getJfastconfig() {
		return jfastConfig;
	}
	
	
	/**
	 * 初始化系统配置
	 */
	public void init(){
		propKit = JfastConfigManeger.getJfastconfigmaneger().getPropKit();
		initJfastConfig();
		initRequestMapping();
		initPlugins();
		initInteceptor();
	}
	
	
	/**
	 * 初始化Jfast配置
	 * @param jFastConfig
	 */
	private void initJfastConfig() {
		ConfigurationProperties configurationProperties = jfastConfig.getClass().getAnnotation(ConfigurationProperties.class);
		String value = configurationProperties.prefix();
		for(Method method : jfastConfig.getClass().getMethods()){
			if(method.getName().startsWith("set")){
				try {
					String key = StrKit.toLowerCaseFirst(method.getName().substring(3));
					method.invoke(jfastConfig, DataType.convert(method.getParameterTypes()[0],propKit.get(value+"."+key)));
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
		
		List<DataSourceConfig> dataSourceConfigs = DataSourceConfigManager.getDataSourceConfigManager().getDataSourceConfigs();
		
		for (DataSourceConfig dataSourceConfig : dataSourceConfigs) {
			DataSourceManager.add(new DbConfig(new DefaultDataSource(dataSourceConfig)));
		}
	}
	
	
	/**
	 * 初始化拦截器
	 */
	private void initInteceptor() {
		List<Class<?>> clazzs = PackageUtil.scanPackage(jfastConfig.getPackageList());
		for(Class<?> cla : clazzs){
			if(cla.isAnnotationPresent(InteceptorConfig.class)){
				inteceptorManager.add((Interceptor)GuiceManager.getGuiceManger().getInjector().getInstance(cla));
				log.debug("add Inteceptor '"+cla.getSimpleName()+"'");
			}
			continue;
		}
	}


	/**
	 * 初始化插件配置
	 */
	private void initPlugins() {
		List<IPlugin> plugins = pluginManger.getPlugins();
		for(IPlugin plugin : plugins){
			try {
				if(!plugin.start()){
					String message = "Plugin start error: " + plugin.getClass().getName();
					log.error(message);
					throw new RuntimeException(message);
				}
			} catch (Exception e) {
				String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage();
				log.error(message, e);
				throw new RuntimeException(message, e);
			}
		}
	}

	/**
	 * 初始化路由映射
	 */
	@SuppressWarnings({ "rawtypes","unchecked" })
	private void initRequestMapping() {
		List<Class<?>> classes = getControllerClass();
		RequestMapping requestMapping = null;
		for(Class cla : classes){
		    requestMapping = (RequestMapping) cla.getAnnotation(RequestMapping.class);
			String controllerKey = requestMapping == null ? "" : requestMapping.value();
			Method[] methods = cla.getDeclaredMethods();
			String actionKey = null;
			for(Method method : methods){
				requestMapping = method.getAnnotation(RequestMapping.class);
				if(requestMapping == null){
					actionKey = Constant.SEPARATOR + StrKit.toLowerCaseFirst(method.getName());
				}else{
					actionKey = method.getAnnotation(RequestMapping.class).value();
				}
				if(StrKit.isNotEmpoty(controllerKey)&& !controllerKey.equals("/")){
					actionKey = controllerKey + actionKey;
				}
				Action action = new Action(actionKey,actionKey,method,cla);
				mapping.put(actionKey, action);
			}
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private List<Class<?>> getControllerClass() {
		List<Class<?>> classes = PackageUtil.scanPackage(jfastConfig.getPackageList());
		for(Class cla : classes){
			Controller controller = (Controller) cla.getAnnotation(Controller.class);
			if(controller != null){
				controllerClass.add(cla);
			}
			continue;
		}
		return controllerClass;
	}

	public Action getAction(String url, String[] urlPara) {
		Action action = mapping.get(url);
		if (action != null) {
			return action;
		}
		
		int i = url.lastIndexOf('/');
		if (i != -1) {
			action = mapping.get(url.substring(0, i));
			urlPara[0] = url.substring(i + 1);
		}
		
		return action;
	}
}
